<template>
  <div id="mapContainer" ref="mapContainer" style="width: 100%;height: 100%;" />
</template>

<script>
import 'ol/ol.css'
import Map from 'ol/Map'
import OSM from 'ol/source/OSM'
import XYZ from 'ol/source/XYZ'
import VectorSource from 'ol/source/Vector'
import Tile from 'ol/layer/Tile'
import VectorLayer from 'ol/layer/Vector'
import LayerGroup from 'ol/layer/Group'
import MVT from 'ol/format/MVT.js'
import VectorTileLayer from 'ol/layer/VectorTile.js'
import VectorTileSource from 'ol/source/VectorTile.js'
import WebGLVectorLayer from 'ol/layer/WebGLVector'
import Style from 'ol/style/Style'
import Circle from 'ol/style/Circle'
import Stroke from 'ol/style/Stroke'
import Icon from 'ol/style/Icon'
import View from 'ol/View'
import Feature from 'ol/Feature'
import Overlay from 'ol/Overlay'
import { Point, LineString } from 'ol/geom'
import { get as getProj } from 'ol/proj'
import { containsCoordinate } from 'ol/extent'
import { defaults as defaultInteractions } from 'ol/interaction'
import Draw, { createBox } from 'ol/interaction/Draw'
import DragInteraction from './map/DragInteraction'
import { fromLonLat, toLonLat } from './map/TransformLonLat'

import { v4 } from 'uuid'
import { getUid } from 'ol'
import { Fill } from 'ol/style'

let olMap, tileLayer = null
export default {
  name: 'MapComponent',
  props: [],
  data() {
    return {
      overlayId: null,
      dragInteraction: new DragInteraction(),
      mapTileList: [],
      mapTileIndex: 0
    }
  },
  created() {
    this.$nextTick(() => {
      this.init()
    })

  },
  mounted() {

  },
  destroyed() {
  },
  methods: {
    init() {
      this.$store.dispatch('server/getMapConfig')
        .then(mapConfigList => {
          if (mapConfigList.length === 0) {
           if (window.mapParam.tilesUrl) {
             this.mapTileList.push({
               tilesUrl: window.mapParam.tilesUrl,
               coordinateSystem: window.mapParam.coordinateSystem
             })
           }
          }else {
            this.mapTileList = mapConfigList
          }
          this.initMap()
      })
    },
    initMap(){
      let center = fromLonLat([116.41020, 39.915119])
      window.coordinateSystem = this.mapTileList[this.mapTileIndex].coordinateSystem
      if (window.mapParam.center) {
        center = fromLonLat(window.mapParam.center)
      }
      const view = new View({
        center: center,
        zoom: window.mapParam.zoom || 10,
        projection: this.projection,
        maxZoom: window.mapParam.maxZoom || 19,
        minZoom: window.mapParam.minZoom || 1
      })

      if (this.mapTileList.length > 0 && this.mapTileList[this.mapTileIndex].tilesUrl) {
        tileLayer = new Tile({
          source: new XYZ({
            projection: getProj('EPSG:3857'),
            wrapX: false,
            tileSize: 256 || window.mapParam.tileSize,
            url: this.mapTileList[this.mapTileIndex].tilesUrl
          })
        })
      } else {
        tileLayer = new Tile({
          preload: 4,
          source: new OSM()
        })
      }
      olMap = new Map({
        interactions: defaultInteractions().extend([this.dragInteraction]),
        target: this.$refs.mapContainer, // 容器ID
        layers: [tileLayer], // 默认图层
        view: view, // 视图
        controls: [ // 控件
        ]
      })
      // olMap.addControl(new ZoomSlider({
      //   className: 'zoom-slider'
      // }))
      olMap.once('loadend', event => {
        this.$emit('loaded')
      })
      olMap.on('click', event => {
        let features = {}
        let layers = {}
        // 单个元素事件传递
        olMap.forEachFeatureAtPixel(event.pixel, (featureAtPixel, layerAtPixel) => {

          if (layerAtPixel) {
            let ol_uid = 'key' + getUid(layerAtPixel)
            layers[ol_uid] = layerAtPixel
            if (Object.hasOwn(features, ol_uid)) {
              features[ol_uid].push(featureAtPixel)
            } else {
              features[ol_uid] = new Array(featureAtPixel)
            }
          }
        })
        // 遍历图层，传递事件
        for (const key in layers) {
          if (Object.hasOwn(layers, key)) {
            var layer = layers[key]
            layer.dispatchEvent({ type: 'click', event: event, features: features[key], outParam: { layersCount: Object.keys(layers).length } });
          }
        }
        features = {}
        layer = {}
      })
      olMap.getView().on('change:resolution', () => {
        this.$emit('zoomChange', olMap.getView().getZoom())
      })
    },
    addVectorTileLayer(tileUrl, clickEvent, errorEvent){
      tileUrl += `?geoCoordSys=${this.mapTileList[this.mapTileIndex].coordinateSystem}&accessToken=${this.$store.getters.token}`
      let source = new VectorTileSource({
        format: new MVT(),
        tileSize: 256,
        url: tileUrl
      })
      let layer = new VectorTileLayer({
        source: source,
        style: function(feature) {
          let status = feature.properties_.gbStatus
          if (layer.get('hideFeatures').includes(feature.properties_.gbId)) {
            return
          }
          if (status === 'ON') {
            return new Style({
              image: new Icon({
                anchor: [0.5, 1],
                crossOrigin: 'Anonymous',
                src: 'static/images/gis/camera1.png',
                opacity: 1
              })
            })
          }else if (status === 'OFF'){
            return new Style({
              image: new Icon({
                anchor: [0.5, 1],
                crossOrigin: 'Anonymous',
                src: 'static/images/gis/camera1-offline.png',
                opacity: 1
              })
            })
          }
        }
        // style: {
        //   // 必须提供 style 配置，可以是对象或函数
        //   'circle-radius': 4,
        //   'circle-fill-color': 'red',
        //   'circle-stroke-color': 'white',
        //   'circle-stroke-width': 0.5
        //   // 'icon-src': 'static/images/gis/sprite.png',
        //   // 'icon-width': 120,
        //   // 'icon-height': 40,
        //   // 'icon-size': [40, 40],
        //   // 'icon-anchor': [0.5, 1],
        //   // 'icon-offset-origin': 'bottom-left',
        //   // 'icon-offset': [
        //   //   'match',
        //   //   ['get', 'status'],
        //   //   'ON',
        //   //   [0, 0],
        //   //   'OFF',
        //   //   [40, 0],
        //   //   'checked',
        //   //   [80, 0],
        //   //   [120, 60]
        //   // ]
        // }
      })
      layer.set('hideFeatures', [])
      olMap.addLayer(layer)
      if (clickEvent) {
        layer.on('click', (event) => {
          console.log(event)
          if (event.features.length > 0) {
            const items = []
            for (let i = 0; i < event.features.length; i++) {
              items.push(event.features[i].properties_)
            }
            clickEvent(items)
          }
        })
      }
      if (errorEvent) {
        source.on('tileloaderror', (event) => {
          console.log(event)
          errorEvent(event)
        })
      }

      return layer
    },
    setCenter(point) {

    },
    getCenter() {
      return toLonLat(olMap.getView().getCenter())
    },
    getZoom() {
      return olMap.getView().getZoom()
    },
    zoomIn() {
      let zoom = olMap.getView().getZoom()
      if (zoom >= olMap.getView().getMaxZoom()) {
        return
      }
      olMap.getView().animate({
        zoom: Math.trunc(zoom) + 1,
        duration: 600
      })
    },
    zoomOut() {
      let zoom = olMap.getView().getZoom()
      if (zoom <= olMap.getView().getMinZoom()) {
        return
      }
      olMap.getView().animate({
        zoom: Math.trunc(zoom) - 1,
        duration: 400
      })
    },
    centerAndZoom(point, zoom, callback) {
      var zoom_ = olMap.getView().getZoom()
      zoom = zoom || zoom_
      var duration = 600
      olMap.getView().setCenter(fromLonLat(point))
      olMap.getView().animate({
        zoom: zoom,
        duration: duration
      })
    },
    coordinateInView: function(point) {
      return containsCoordinate(olMap.getView().calculateExtent(), fromLonLat(point))
    },
    panTo(point, zoom, endCallback) {
      const duration = 1500
      var coordinate = fromLonLat(point)
      if (containsCoordinate(olMap.getView().calculateExtent(), coordinate)) {
        olMap.getView().setCenter(coordinate)
        if (zoom !== olMap.getView().getZoom()) {
          olMap.getView().setZoom(zoom)
        }
        if (endCallback) {
          endCallback()
        }
        return
      }

      olMap.getView().cancelAnimations()
      olMap.getView().animate({
        center: coordinate,
        duration: duration
      })
      olMap.getView().animate({
        zoom: zoom -2,
        duration: duration / 2
      }, {
        zoom: zoom || olMap.getView().getZoom(),
        duration: duration / 2
      })
      setTimeout(endCallback, duration + 100)
    },
    fit(layer) {
      const extent = layer.getSource().getExtent()
      if (extent) {
        olMap.getView().fit(extent, {
          duration: 600,
          padding: [100, 100, 100, 100]
        })
      }
    },
    openInfoBox(position, content, offset) {
      if (this.overlayId !== null) {
        this.closeInfoBox(this.overlayId)
        this.overlayId = null
      }
      const id = v4()
      // let infoBox = document.createElement('div')
      // infoBox.setAttribute('id', id)
      // infoBox.innerHTML = content
      const overlay = new Overlay({
        id: id,
        autoPan: true,
        autoPanAnimation: {
          duration: 250
        },
        element: content,
        positioning: 'bottom-center',
        offset: offset,
        position: fromLonLat(position)
        // className:overlayStyle.className
      })
      olMap.addOverlay(overlay)
      this.overlayId = id
      return id
    },
    closeInfoBox(id) {
      let overlay = olMap.getOverlayById(id)
      if (overlay) {
        olMap.removeOverlay(overlay)
      }
      let element = document.getElementById(id)
      if (element) {
        element.remove()
      }
    },
    /**
     * 添加图层， 数据坐标系由控件内完成，输入和输出永远是wgs84
     * [
     *     {
     *
     *         position: [119.1212,45,122],
     *             image: {
     *                 src:"/images/123.png",
     *                 anchor: [0.5, 0.5]
     *
     *             }
     *     }
     *
     * ]
     * @param data
     * @param clickEvent
     * @param option
     */
    addPointLayer(data, clickEvent, option) {
      let vectorLayer = this.createPointLayer(data, clickEvent, option)
      olMap.addLayer(vectorLayer)
      return vectorLayer
    },
    createPointLayer(data, clickEvent, option){
      const features = []
      let maxZoom = (option && option.maxZoom) ? option.maxZoom : olMap.getView().getMaxZoom()
      let minZoom = (option && option.minZoom) ? option.minZoom : olMap.getView().getMinZoom()
      let declutter = option && option.declutter
      if (data.length > 0) {
        for (let i = 0; i < data.length; i++) {
          const feature = new Feature(new Point(fromLonLat(data[i].position)))
          feature.setId(data[i].id)
          feature.customData = data[i].data
          feature.setProperties({
            status: data[i].status
          })
          features.push(feature)
        }
      }
      const source = new VectorSource()
      if (features.length > 0) {
        source.addFeatures(features)
      }
      const vectorLayer = new WebGLVectorLayer({
        source: source,
        maxZoom: maxZoom,
        minZoom: minZoom,
        style: {
          // 必须提供 style 配置，可以是对象或函数
          // 'circle-radius': 10,
          // 'circle-fill-color': 'red',
          // 'circle-stroke-color': 'white',
          // 'circle-stroke-width': 0.5
          'icon-src': 'static/images/gis/sprite.png',
          'icon-width': 120,
          'icon-height': 40,
          'icon-size': [40, 40],
          'icon-anchor': [0.5, 1],
          'icon-offset-origin': 'bottom-left',
          'icon-offset': [
            'match',
            ['get', 'status'],
            'ON',
            [0, 0],
            'OFF',
            [40, 0],
            'checked',
            [80, 0],
            [120, 60]
          ]
        }
      })
      if (clickEvent && typeof clickEvent === 'function') {
        vectorLayer.on('click', (event) => {
          console.log(event)
          if (event.features.length > 0) {
            const items = []
            for (let i = 0; i < event.features.length; i++) {
              items.push(event.features[i].customData)
            }
            clickEvent(items)
          }
        })
      }
      return vectorLayer

    },
    createPointLayer2(data, clickEvent, option){
      if (data.length > 0) {
        const features = []
        let maxZoom = (option && option.maxZoom) ? option.maxZoom : olMap.getView().getMaxZoom()
        let minZoom = (option && option.minZoom) ? option.minZoom : olMap.getView().getMinZoom()
        let declutter = option && option.declutter

        for (let i = 0; i < data.length; i++) {
          const feature = new Feature(new Point(fromLonLat(data[i].position)))
          feature.setId(data[i].id)
          feature.customData = data[i].data
          const style = new Style()
          style.setImage(new Icon({
            anchor: data[i].image.anchor,
            crossOrigin: 'Anonymous',
            src: data[i].image.src,
            opacity: 1
          }))
          feature.setStyle(style)
          features.push(feature)
        }
        const source = new VectorSource()
        source.addFeatures(features)
        const vectorLayer = new VectorLayer({
          source: source,
          renderMode: 'image',
          declutter: declutter,
          maxZoom: maxZoom,
          minZoom: minZoom
        })
        if (clickEvent && typeof clickEvent === 'function') {
          vectorLayer.on('click', (event) => {

            if (event.features.length > 0) {
              const items = []
              for (let i = 0; i < event.features.length; i++) {
                items.push(event.features[i].customData)
              }
              clickEvent(items)
            }
          })
        }

        return vectorLayer
      }
    },
    hasFeature (layer, id) {
      if (layer instanceof LayerGroup) {
        // 目前LayerGroup的情况肯定含有这个
        return true
      }else {
        if (layer.getSource().getFeatureById(id)) {
          return true
        }
      }
      return false
    },
    addFeature (layer, data) {

      const feature = new Feature(new Point(fromLonLat(data.position)))
      feature.setId(data.id)
      feature.customData = data.data
      feature.setProperties({
        status: data.status
      })
      layer.getSource().addFeature(feature)
    },
    updatePointLayer(layer, data, postponement) {
      console.log(data)
      layer.getSource().clear(true)
      if (!data || data.length === 0) {
        return
      }
      const features = []
      for (let i = 0; i < data.length; i++) {
        const feature = new Feature(new Point(fromLonLat(data[i].position)))
        feature.setId(data[i].id)
        feature.customData = data[i].data
        feature.setProperties({
          status: data[i].status
        })
        features.push(feature)
      }
      layer.getSource().addFeatures(features)
      if (postponement) {
        olMap.removeLayer(layer)
        setTimeout(() => {
          olMap.addLayer(layer)
        }, 100)
      }
      return layer
    },
    addPointLayerGroup(data, clickEvent) {

      let keys = Array.from(data.keys())

      let layers = []
      for (let i = 0; i < keys.length; i++) {
        let zoom = keys[i]
        console.log(zoom)
        let vectorLayer = this.createPointLayer(data.get(zoom), clickEvent, {
          minZoom : zoom
        })
        vectorLayer.setProperties('layerId', zoom)
        if (vectorLayer) {
          layers.push(vectorLayer)
        }
      }
      let groupLayer = new LayerGroup({
        layers: layers
      })
      olMap.addLayer(groupLayer)
      return groupLayer
    },
    updatePointLayerGroup(layer, data, postponement) {

    },

    removeLayer(layer) {
      olMap.removeLayer(layer)
    },
    clearLayer(layer) {
      layer.getSource().clear(true)
    },
    setFeatureImageById(layer, featureId, image) {
      let feature = layer.getSource().getFeatureById(featureId)
      if (!feature) {
        console.error('更改feature的图标时未找到图标')
        return
      }
      let style = feature.getStyle()
      style.setImage(new Icon({
        anchor: image.anchor,
        crossOrigin: 'Anonymous',
        src: image.src
      }))
      feature.setStyle(style)
      olMap.render()
    },
    hideFeature(layer, featureId) {
      layer.get('hideFeatures').add(featureId)
    },
    cancelHideFeature(layer, featureId) {
      let index = layer.get('hideFeatures').indexOf(featureId)
      layer.get('hideFeatures').splice(index, 1)
    },
    setFeaturePositionById(layer, featureId, data) {

      let featureOld = layer.getSource().getFeatureById(featureId)
      if (featureOld) {
        layer.getSource().removeFeature(featureOld)
      }

      const feature = new Feature(new Point(fromLonLat(data.position)))
      feature.setId(data.id)
      feature.customData = data.data
      feature.setProperties({
        status: data.status
      })
      layer.getSource().addFeature(feature)
    },

    addLineLayer(positions) {
      if (positions.length > 0) {
        const points = []
        for (let i = 0; i < positions.length; i++) {
          points.push(fromLonLat(positions[i]))
        }
        const line = new LineString(points)
        const lineFeature = new Feature(line)
        lineFeature.setStyle(new Style({
          stroke: new Stroke({
            width: 4,
            color: '#0c6d6a'
          })
        }))
        const source = new VectorSource()
        source.addFeature(lineFeature)
        const vectorLayer = new VectorLayer({
          source: source
        })
        olMap.addLayer(vectorLayer)
        return vectorLayer
      }
    },
    getCurrentCoordinateSystem() {
      return this.mapTileList[this.mapTileIndex].coordinateSystem
    },
    changeMapTile(index) {
      let center = this.getCenter()
      let mapTileConfig = this.mapTileList[this.mapTileIndex]
      this.mapTileIndex = index
      window.coordinateSystem = this.mapTileList[this.mapTileIndex].coordinateSystem
      tileLayer.getSource().clear()
      tileLayer.getSource().setUrl(this.mapTileList[index].tilesUrl)
      if (mapTileConfig.coordinateSystem !== this.mapTileList[this.mapTileIndex].coordinateSystem) {
        // 发送通知
        this.$emit('coordinateSystemChange', this.mapTileList[this.mapTileIndex].coordinateSystem)
        // 修正地图的中心点
        olMap.getView().setCenter(fromLonLat(center))
      }
    },
    getZoomExtent(){
      return [olMap.getView().getMinZoom(), olMap.getView().getMaxZoom()]
    },
    /**
     * 根据距离计算经纬度差值，方便前端抽稀计算
     * @param distance 距离， 单位：像素值
     * @param zoom 地图层级，默认取值当前层级
     */
    computeDiff(distance, zoom) {
      if (!distance) {
        return []
      }
      let resolution
      if (!zoom) {
        resolution = olMap.getView().getResolution()
      }else {
        resolution = olMap.getView().getResolutionForZoom(zoom)
      }
      let diff = resolution * distance
      return toLonLat([diff, diff])[0]


      // let extent = olMap.getView().calculateExtent(olMap.getSize())
      //
      //
      // let minLng = extent[0]
      // let maxLng = extent[2]
      // let minLat = extent[1]
      // let maxLat = extent[3]
      //
      // let style = new Style({
      //   stroke: new Stroke({
      //     width: 1,
      //     color: 'rgba(65,65,65,0.8)'
      //   })
      // })
      // const source = new VectorSource()
      // let lng = minLng
      // while (lng <= maxLng) {
      //
      //   const points = [[lng, minLat], [lng, maxLat]]
      //   const line = new LineString(points)
      //   const lineFeature = new Feature(line)
      //   lineFeature.setStyle(style)
      //   source.addFeature(lineFeature)
      //   lng += diff
      // }
      //
      // let lat = minLat
      // while (lat <= maxLat) {
      //
      //   const points = [[minLng, lat], [maxLng, lat]]
      //   console.log(points)
      //   const line = new LineString(points)
      //   const lineFeature = new Feature(line)
      //   lineFeature.setStyle(style)
      //   source.addFeature(lineFeature)
      //   lat += diff
      // }
      //
      // const vectorLayer = new VectorLayer({
      //   source: source
      // })
      // olMap.addLayer(vectorLayer)
    },
    startDrawBox(callback) {

      const source = new VectorSource({ wrapX: false })

      const vectorLayer = new VectorLayer({
        source: source,
        style: new Style({
          fill: new Fill({
            color: 'rgba(255, 97, 97, 0.24)'
          }),
          stroke: new Stroke({
            color: 'rgba(255, 97, 97, 0.84)',
            width: 0
          })
        })
      })
      olMap.addLayer(vectorLayer)
      let draw = new Draw({
        source: source,
        type: 'Circle',
        geometryFunction: createBox(),
        style: new Style({
          fill: new Fill({
            color: 'rgba(255, 97, 97, 0.24)'
          }),
          stroke: new Stroke({
            color: 'rgba(255, 97, 97, 0.84)',
            width: 0
          }),
          freehand: true
        })
      })
      olMap.addInteraction(draw)
      // 添加事件
      draw.on('drawstart', function (event) {
        source.clear()
      })
      draw.on('drawend', function (event) {
        let geometry = event.feature.getGeometry()
        let extent = geometry.getExtent()
        let min = toLonLat([extent[0], extent[1]])
        let max = toLonLat([extent[2], extent[3]])

        callback([min[0], min[1], max[0], max[1]])
        draw.abortDrawing()
        olMap.removeInteraction(draw)
        source.clear(true)
        olMap.removeLayer(vectorLayer)
      })
    },
    getCoordSys(){
      return this.mapTileList[this.mapTileIndex].coordinateSystem
    },
    refreshLayer(layer){
      if (layer && layer.getSource()) {
        layer.getSource().refresh()
      }
    }
  }
}
</script>

<style>
#mapContainer .zoom-slider {
  width: 14px;
  height: 200px;
  right: 20px;
  bottom: 400px;
  border-bottom: 1px #dfdfdf solid;
  border-right: 1px #dfdfdf solid;
  cursor: pointer;
  background-color: #FFFFFF;
  border-radius: 3px;

}
#mapContainer .zoom-slider button {
  position: relative;
  width: 10px;
  height: 10px;
  border-radius: 5px;
  margin: 0;
  background-color: #606266;
}
</style>
